package com.xyrl.starter.lock.redis.lock;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author zhongp
 * @title: RedisDistributedLock
 * @projectName xyrl
 * @description: TODO
 * @date 2019/12/09 15:57
 */
@Slf4j
public class RedisDistributedLock extends AbstractDistributedLock {

    private RedissonClient redissonClient;

    public RedisDistributedLock(RedissonClient redissonClient) {
        super();
        this.redissonClient = redissonClient;
    }

    @Override
    public boolean lock(String key, long expire, int retryTimes, long sleepMillis) {
        boolean result = setRedis(key, expire);
        // 如果获取锁失败，按照传入的重试次数进行重试
        while ((!result) && retryTimes-- > 0) {
            try {
                log.debug("lock failed, retrying..." + retryTimes);
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                return false;
            }
            result = setRedis(key, expire);
        }
        return result;
    }

    private boolean setRedis(String key, long expire) {
        String uuid = UUID.randomUUID().toString();
        return setRedis(key, uuid, expire);
    }

    private boolean setRedis(String key, String value, long expire) {
        RLock rLock = redissonClient.getLock(key);
        log.debug("原始传入上锁时间:{}", expire);
        long temp = expire / 3;
        if (temp == 0) {
            temp = 1;
        }
        expire = temp * 3;
        //是否加锁成功
        boolean isLock = rLock.tryLock();
        log.debug("取得加锁状态：{},最终超时间：{}", isLock, expire);
        if (isLock) {
            rLock.lock(expire, TimeUnit.MILLISECONDS);
        } else {
            return false;
        }
        return isLock;
    }

    @Override
    public boolean releaseLock(String key) {
        return releaseLock(key, "");
    }

    @Override
    public boolean lockWithoutWaiting(String key) {
        String uuid = UUID.randomUUID().toString();
        return lockWithoutWaiting(key, uuid, TIMEOUT_MILLIS);
    }

    @Override
    public boolean lockWithoutWaiting(String key, String value) {
        return lockWithoutWaiting(key, value, TIMEOUT_MILLIS);
    }

    @Override
    public boolean lockWithoutWaiting(String key, String value, long expire) {
        return setRedis(key, value, expire);
    }

    @Override
    public boolean releaseLock(String key, String value) {
        try {
            log.debug("开始执行解锁:{}", key);
            RLock rLock = redissonClient.getLock(key);
            //判断是否锁定
            if(rLock.isLocked()){
                //判断是否是当前线程锁定
                if(rLock.isHeldByCurrentThread()){
                    //解锁
                    rLock.unlockAsync();
                    rLock.unlock();
                }
            }
            return true;
        } catch (Exception e) {
            log.error("解锁失败：{}", e);
            return false;
        }
    }
}
